//---------------------------------------------------------------------------
//                       GHI Electronics, LLC
//                   http://www.ghielectronics.com
//                        Copyright (c) 2010
//                        All rights reserved
//                          EMX Demo V1.01
//---------------------------------------------------------------------------
/*
 * You can use this file if you agree to the following:
 *
 * 1. This header can't be changed under any condition.
 * 2. This is a free software and therefore is provided with NO warranty.
 * 3. Feel free to modify the code but we ask you to provide us with
 *	  any bug reports so we can keep the code up to date.
 * 4. This code may ONLY be used with GHI Electronics, LLC products.
 *
 * THIS SOFTWARE IS PROVIDED BY GHI ELECTRONICS, LLC ``AS IS'' AND 
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
 * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL 
 * GHI ELECTRONICS, LLC BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR ORT 
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
 *
 *	Specs are subject to change without any notice
 */

/*
Pyxis 2.0
All methods marked with Thomas W. Holtquist or Skewworks fall under this license
Copyright 2010-2011 Thomas W. Holtquist

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

using System;
using System.Runtime.CompilerServices;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using System.Threading;
using GHIElectronics.NETMF.Hardware;

namespace Skewworks.Pyxis.Drivers
{
    public static class VS1053
    {

        #region Constants

        // values
        const ushort SM_SDINEW = 0x800;
        const ushort SM_RESET = 0x04;
        const ushort SC_MULT_7 = 0xE000;
        const ushort VOL_MUTE = 0xFEFE;

        // Registers
        const int SCI_MODE = 0x00;
        const int SCI_BASS = 0x02;
        const int SCI_CLOCKF = 0x03;
        const int SCI_DECODE_TIME = 0x04;
        const int SCI_AUDATA = 0x05;
        const int SCI_VOL = 0x0B;

        #endregion

        #region Variables

        // Some GPIO pins
        static private OutputPort reset;
        static private InputPort DREQ;

        // Define SPI Configuration for VS1053 MP3 decoder
        static private SPI.Configuration dataConfig = new SPI.Configuration((Cpu.Pin)15, false, 0, 0, false, true, 3000, SPI.SPI_module.SPI1);
        static private SPI.Configuration cmdConfig = new SPI.Configuration((Cpu.Pin)68, false, 0, 0, false, true, 3000, SPI.SPI_module.SPI1);
        static private SPI spi;

        // Bass values
        static private byte btBassAmp = 0;
        static private byte btBassFrq = 0;

        // Treble values
        static private byte btTrebAmp = 0;
        static private byte btTrebFrq = 0;

        static private bool isInitialized = false;
        static private byte[] block = new byte[32];
        static private byte[] cmdBuffer = new byte[4];

        #endregion

        #region Properties

        public static bool Initialized
        {
            get { return isInitialized; }
        }

        #endregion

        #region Public Methods

        [MethodImpl(MethodImplOptions.Synchronized)]
        public static void Shutdown()
        {
            if (isInitialized)
            {
                Reset();

                spi.Dispose();
                reset.Dispose();
                DREQ.Dispose();

                isInitialized = false;
            }
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public static void Initialize()
        {
            if (isInitialized)
                Shutdown();

            spi = new SPI(cmdConfig);
            reset = new OutputPort((Cpu.Pin)31, true);
            DREQ = new InputPort((Cpu.Pin)71, false, Port.ResistorMode.PullUp);

            isInitialized = true;

            Reset();

            Command_Write(SCI_MODE, SM_SDINEW);
            Command_Write(SCI_CLOCKF, SC_MULT_7);
            //Command_Write(SCI_VOL, 0);  // highest volume

            //if (Command_Read(SCI_VOL) != (0))
            //{
            //    throw new Exception("Failed to initialize VS1053 encoder.");
            //}
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public static void SendData(byte[] data)
        {
            int size = data.Length - data.Length % 32;

            spi.Config = dataConfig;
            for (int i = 0; i < size; i += 32)
            {

                while (DREQ.Read() == false)
                    Thread.Sleep(1);  // wait till done

                Array.Copy(data, i, block, 0, 32);

                spi.Write(block);
            }
        }

        /// <summary>
        /// Sets the volume
        /// Range 0 - 100
        /// Modifided by: Thomas W. Holtquist (http://www.skewworks.com)
        /// </summary>
        /// <param name="volume">0 (Mute) - 100 (Full)</param>
        [MethodImpl(MethodImplOptions.Synchronized)]
        public static void SetVolume(byte volume)
        {
            if (volume < 0)
                volume = 0;
            if (volume > 100)
                volume = 100;

            ushort vol = (ushort)(65278 - (65278 * ((float)volume / 100)));
            Command_Write(SCI_VOL, vol);  // highest volume
        }

        /// <summary>
        /// Sets the Bass control on the VS1053 chip
        /// ALL values range 0 - 15
        /// Thomas W. Holtquist (http://www.skewworks.com)
        /// </summary>
        /// <param name="BassAmplitude">1.5dB steps, 0 = off</param>
        /// <param name="BassFrequencyLimit">Lower limit Frequency 10Hz steps</param>
        /// <param name="TrebleAmplitude">1dB steps, 0 = off</param>
        /// <param name="TrebleFrequencyLimit">Lower limit Frequency 1000Hz steps</param>
        [MethodImpl(MethodImplOptions.Synchronized)]
        public static void SetBassAndTreble(byte BassAmplitude, byte BassFrequencyLimit, byte TrebleAmplitude, byte TrebleFrequencyLimit)
        {
            // Preserve Max value
            if (BassAmplitude > 15) BassAmplitude = 15;
            if (BassFrequencyLimit > 15) BassFrequencyLimit = 15;
            if (TrebleAmplitude > 15) TrebleAmplitude = 15;
            if (TrebleFrequencyLimit > 15) TrebleFrequencyLimit = 15;

            // Copy out
            btBassAmp = BassAmplitude;
            btBassFrq = BassFrequencyLimit;
            btTrebAmp = TrebleAmplitude;
            btTrebFrq = TrebleFrequencyLimit;

            // PerForm actual command
            SetSCIBass();
        }

        #endregion

        #region Private Methods

        private static void Reset()
        {
            while (DREQ.Read() == false) ;

            Command_Write(SCI_MODE, (ushort)(Command_Read(SCI_MODE) | SM_RESET));
            Thread.Sleep(1);
            while (DREQ.Read() == false) ;

        }

        static private void Command_Write(byte address, ushort data)
        {
            while (DREQ.Read() == false) ;

            spi.Config = cmdConfig;
            cmdBuffer[0] = 0x02;
            cmdBuffer[1] = address;
            cmdBuffer[2] = (byte)(data >> 8);
            cmdBuffer[3] = (byte)data;

            spi.Write(cmdBuffer);

        }

        static private ushort Command_Read(byte address)
        {
            ushort temp;

            while (DREQ.Read() == false) ;

            spi.Config = cmdConfig;
            cmdBuffer[0] = 0x03;
            cmdBuffer[1] = address;
            cmdBuffer[2] = 0;
            cmdBuffer[3] = 0;

            spi.WriteRead(cmdBuffer, cmdBuffer, 2);

            temp = cmdBuffer[0];
            temp <<= 8;
            temp += cmdBuffer[1];

            return temp;
        }

        /// <summary>
        /// PerForms the actual SCI Bass command based of private variables
        /// Thomas W. Holtquist (http://www.skewworks.com)
        /// </summary>
        private static void SetSCIBass()
        {
            // Convert stored values into ushort for command
            ushort BassVal = btTrebAmp;
            BassVal = (ushort)((BassVal << 4) + btTrebFrq);
            BassVal = (ushort)((BassVal << 4) + btBassAmp);
            BassVal = (ushort)((BassVal << 4) + btBassFrq);

            // Process Command
            Command_Write(SCI_BASS, BassVal);
        }

        #endregion

    }

}
